package gameoflife.gui;

import gameoflife.Pattern;
import gameoflife.Rule;
import gameoflife.RunManager;
import gameoflife.RunManager.State;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.io.File;

import javax.swing.JCheckBoxMenuItem;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.KeyStroke;
import javax.swing.filechooser.FileNameExtensionFilter;

@SuppressWarnings("serial")
public class MenuBar extends JMenuBar {

	private static final String PATTERNS_PATH = "./src/main/resources/gameoflife/patterns";

	public MenuBar(final JFrame frame, final RunManager runManager, final Display display) {
		super();

		JMenu fileMenu = new JMenu("File");
		fileMenu.setMnemonic(KeyEvent.VK_F);

		JMenu gameMenu = new JMenu("Game");
		gameMenu.setMnemonic(KeyEvent.VK_G);

		JMenu viewMenu = new JMenu("View");
		viewMenu.setMnemonic(KeyEvent.VK_V);

		// open item
		JMenuItem openItem = new JMenuItem("Open...", KeyEvent.VK_O);
		openItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, InputEvent.CTRL_DOWN_MASK));
		final JFileChooser fc = new JFileChooser(PATTERNS_PATH);
		openItem.addActionListener(new ActionListener() {			
			@Override
			public void actionPerformed(ActionEvent event) {
				fc.setFileFilter(new FileNameExtensionFilter("Life Patterns (.lif, .rle)", "lif", "rle"));
				if (fc.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
					File file = fc.getSelectedFile();
					fc.setCurrentDirectory(file);
					Pattern pattern;
					try {
						pattern = Pattern.createFromFile(file);
					} catch (Exception e) {
						JOptionPane.showMessageDialog(frame, "Unable to open file: " + file.getName());
						return;
					}

					display.getAutomaton().clear();
					display.getAutomaton().setRule(pattern.getRule());
					display.getAutomaton().addAll(pattern.getPositions());
					display.fitToScreen();

					runManager.setPattern(pattern);
					frame.setTitle(frame.getTitle());
				}
			}
		});

		// save item
		JMenuItem saveItem = new JMenuItem("Save...", KeyEvent.VK_S);
		saveItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK));
		saveItem.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				if (display.getAutomaton().size() > 0) {
					fc.setFileFilter(new FileNameExtensionFilter("RLE (.rle)", ".rle"));
					if (fc.showSaveDialog(null) == JFileChooser.APPROVE_OPTION) {
						File file = fc.getSelectedFile();
						fc.setCurrentDirectory(file);

						Pattern pattern = display.getAutomaton().toPattern();
						pattern.saveToFile(file.getAbsolutePath());

						runManager.setPattern(pattern);
						frame.setTitle(frame.getTitle());
					}
				}
			}
		});

		// run item
		JMenuItem runStopItem = new JMenuItem("") {
			@Override
			public String getText() {
				return runManager.getState() == State.RUNNING ? "Stop" : "Start";
			}
		};
		runStopItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0));
		runStopItem.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				runManager.toggle();
			}
		});

		// next step item
		JMenuItem nextStepItem = new JMenuItem("Next Step", KeyEvent.VK_P);
		nextStepItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_P, KeyEvent.CTRL_DOWN_MASK));
		nextStepItem.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				runManager.step();
			}
		});

		// next generation item
		JMenuItem nextGenerationItem = new JMenuItem("Next Generation", KeyEvent.VK_G);
		nextGenerationItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_G, InputEvent.CTRL_DOWN_MASK));
		nextGenerationItem.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				runManager.step(1);
			}
		});

		// reset item
		JMenuItem resetItem = new JMenuItem("Reset", KeyEvent.VK_R);
		resetItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, InputEvent.CTRL_DOWN_MASK));
		resetItem.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				display.getAutomaton().clear();
				Pattern pattern = runManager.getPattern();
				if (pattern != null) {
					display.getAutomaton().addAll(pattern.getPositions());
					display.fitToScreen();
				}
			}
		});

        // speed
        JMenuItem fasterItem = new JMenuItemDisabledAccelerator("Faster", KeyEvent.VK_F);
        fasterItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_PLUS, 0));
        fasterItem.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				runManager.speedUp();
			}
		});
		JMenuItem slowerItem = new JMenuItemDisabledAccelerator("Slower", KeyEvent.VK_S);
        slowerItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, 0));
        slowerItem.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				runManager.speedDown();
			}
		});

		// new item
		JMenuItem newItem = new JMenuItem("New", KeyEvent.VK_N);
		newItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, InputEvent.CTRL_DOWN_MASK));
		newItem.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				display.recenter();
				display.getAutomaton().clear();
				runManager.setPattern(null);
				frame.setTitle(frame.getTitle());
			}
		});

		// named rules
		JMenu rulesMenu = new JMenu("Named Rules");
		rulesMenu.setMnemonic(KeyEvent.VK_N);
		class RuleItem extends JMenuItem {
			RuleItem(final Rule rule) {
				setText(rule.toString());
				addActionListener(new ActionListener() {
					@Override
					public void actionPerformed(ActionEvent e) {
						display.getAutomaton().setRule(rule);
					}
				});
			}
		}
		for (Rule rule : Rule.RULES) {
			rulesMenu.add(new RuleItem(rule));
		}

		// set rule
		JMenuItem setRule = new JMenuItem("Set Rule...", KeyEvent.VK_U);
		setRule.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				StatusBar.getInstance().editRule();
			}
		});

		// quit item
		JMenuItem quitItem = new JMenuItem("Quit", KeyEvent.VK_Q);
		quitItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q, InputEvent.CTRL_DOWN_MASK));
		quitItem.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				System.exit(0);
			}
		});

		// auto fit
		final JCheckBoxMenuItem autoFitItem = new JCheckBoxMenuItem("Auto Fit");
		autoFitItem.setMnemonic(KeyEvent.VK_T);
		autoFitItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_T, InputEvent.CTRL_DOWN_MASK));
		autoFitItem.setSelected(true);
		display.setAutoFitButton(autoFitItem);

		// fit pattern
		final JMenuItem fitPatternItem = new JMenuItem("Fit Pattern", KeyEvent.VK_F);
		fitPatternItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F, InputEvent.CTRL_DOWN_MASK));
		fitPatternItem.addActionListener(new ActionListener() {			
			@Override
			public void actionPerformed(ActionEvent e) {
				display.fitToScreen();
			}
		});

		// move to origin
		final JMenuItem moveToOriginItem = new JMenuItem("Move To Origin", KeyEvent.VK_M);
		moveToOriginItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_M, InputEvent.CTRL_DOWN_MASK));
		moveToOriginItem.addActionListener(new ActionListener() {			
			@Override
			public void actionPerformed(ActionEvent e) {
				display.recenter();
			}
		});

		// zoom
		JMenuItem zoomInItem = new JMenuItemDisabledAccelerator("Zoom In", KeyEvent.VK_I);
		zoomInItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_UP, InputEvent.CTRL_DOWN_MASK));
		zoomInItem.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				display.zoom(1);
			}
		});
		JMenuItem zoomOutItem = new JMenuItemDisabledAccelerator("Zoom Out", KeyEvent.VK_O);
		zoomOutItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, InputEvent.CTRL_DOWN_MASK));
		zoomOutItem.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				display.zoom(-1);
			}
		});

		// full-screen
		JMenuItem fullScreenItem = new JMenuItem("Full Screen", KeyEvent.VK_S);
		fullScreenItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F11, 0));
		fullScreenItem.addActionListener(new ActionListener() {

			private boolean fullScreen = false;
			private int restoreWidth, restoreHeight, restoreX, restoreY;

			@Override
			public void actionPerformed(ActionEvent e) {				
				if (fullScreen = !fullScreen) {
					restoreWidth = frame.getWidth();
					restoreHeight = frame.getHeight();
					restoreX = frame.getX();
					restoreY = frame.getY();
					frame.dispose();
					frame.setUndecorated(true);
					frame.setResizable(false);
					frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
					frame.setVisible(true);
				}
				else {
					frame.dispose();
					frame.setUndecorated(false);
					frame.setResizable(true);
					frame.setExtendedState(JFrame.NORMAL);
					frame.setBounds(restoreX, restoreY, restoreWidth, restoreHeight);
					frame.setVisible(true);
				}
			}
		});

		JCheckBoxMenuItem showGridLines = new JCheckBoxMenuItem("Show Grid Lines");
		showGridLines.setMnemonic(KeyEvent.VK_L);
		showGridLines.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_L, KeyEvent.CTRL_DOWN_MASK));
		showGridLines.setSelected(display.getVisibleGridLines());
		showGridLines.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				display.setVisibleGridLines(!display.getVisibleGridLines());
			}
		});

		add(fileMenu);
		add(gameMenu);
		add(viewMenu);

		fileMenu.add(newItem);
		fileMenu.add(openItem);
		fileMenu.add(saveItem);
		fileMenu.addSeparator();
		fileMenu.add(quitItem);

		gameMenu.add(runStopItem);
		gameMenu.add(nextStepItem);
		gameMenu.add(nextGenerationItem);
		gameMenu.add(resetItem);
		gameMenu.addSeparator();
		gameMenu.add(fasterItem);
		gameMenu.add(slowerItem);
		gameMenu.addSeparator();
		gameMenu.add(setRule);
		gameMenu.add(rulesMenu);

		viewMenu.add(fitPatternItem);
		viewMenu.add(autoFitItem);
		viewMenu.add(moveToOriginItem);
		viewMenu.addSeparator();
		viewMenu.add(zoomInItem);
		viewMenu.add(zoomOutItem);
		viewMenu.addSeparator();
		viewMenu.add(showGridLines);
		viewMenu.addSeparator();
		viewMenu.add(fullScreenItem);
	}

	private class JMenuItemDisabledAccelerator extends JMenuItem {

		public JMenuItemDisabledAccelerator(String text, int mnemonic) {
			super(text, mnemonic);
		}

		@Override
		public void setAccelerator(KeyStroke keyStroke) {
			super.setAccelerator(keyStroke);
			getInputMap(WHEN_IN_FOCUSED_WINDOW).put(keyStroke, "none");
		}			
	}

}
